﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Divimast.NavSelector
{
	public class NavVersion
	{
		public NavVersion(string rtc)
		{
			this.FilePath = rtc;
			this.FolderPath = Path.GetDirectoryName(this.FilePath);
			this.Info = FileVersionInfo.GetVersionInfo(rtc);
			this.Description = string.Format("{0} ({1})", this.Info.ProductVersion, this.Info.FileName);
			this.Version = this.Info.ProductVersion;
			switch (this.Info.ProductMajorPart)
			{
				case 6: // NAV2009
					this.Interface = new Guid("{E3041209-EDEA-36B5-ADE4-51E7751BE082}");
					this.TypeLib = new Guid("{5020AC1E-A4F0-402B-A920-3FED4E3B05CC}");
					this.Command = string.Format(@"{0} ""%1""", this.FilePath);
					break;
				case 7: // NAV2013
					this.Interface = new Guid("{14519985-4959-4F7C-AC30-CBBCD9DFBC08}");
					this.TypeLib = new Guid("{5020AC1E-A4F0-402B-A920-3FED4E3B05CC}");
					this.Command = string.Format(@"{0} -protocolhandler ""%1""", this.FilePath);
					if (this.Info.ProductMinorPart == 1 && this.Info.ProductBuildPart >= 36035)
					{
						this.Interface = new Guid("{59521B62-D441-47E6-8224-A07203686BA2}");
					}
					break;
			}
			this.RegAsm = this.GetRegAsmPath();
		}

		public string Version { get; private set; }
		public string Description { get; private set; }
		public string FilePath { get; private set; }
		public string FolderPath { get; private set; }
		public string Command { get; private set; }
		public FileVersionInfo Info { get; private set; }

		public Guid Interface { get; private set; }
		public Guid TypeLib { get; private set; }

		public string RegAsm { get; private set; }

		private string GetRegAsmPath()
		{
			//// This is the location of the .Net Framework Registry Key
			//const string framworkRegPath = @"Software\Microsoft\.NetFramework";

			//// Get a non-writable key from the registry
			//var netFramework = Registry.LocalMachine.OpenSubKey(framworkRegPath, false);

			//if (netFramework == null)
			//{
			//  throw new Exception(".Net Framework not installed");
			//}
			//// Retrieve the install root path for the framework
			//var installRoot = netFramework.GetValue("InstallRoot").ToString();

			// must use 32-bit RegAsm
			//const string installRoot = @"C:\Windows\Microsoft.NET\Framework64";
			const string installRoot = @"C:\Windows\Microsoft.NET\Framework";

			// default version --> 2.0.50727
			var version = "v2.0.50727";
			if (this.Info.ProductMajorPart == 7 && this.Info.ProductMinorPart == 1)
			{
				// from NAV 2013 R2 --> 4.0.30319
				version = "v4.0.30319";
			}

			var path = Path.Combine(installRoot, version);
			path = Path.Combine(path, "RegAsm.exe");
			if (File.Exists(path))
			{
				return path;
			}
			throw new Exception(string.Format("RegAsm not found in {0}", path));
		}
	}

	public partial class Main : Form
	{
		private const string NAV2009 = "60";
		private const string NAV2013 = "70";
		private const string NAV2013R2 = "71";

		public Main()
		{
			InitializeComponent();
			LoadNavVersions();
		}

		private void LoadNavVersions()
		{
			this.listVersion.Items.Clear();
			this.listVersion.DisplayMember = "Description";

			var root = Registry.LocalMachine.OpenSubKey(this.NavRootKey);

			if (root == null)
			{
				return;
			}

			var paths = new List<string>();
			// look for every RoleTailored Client installed
			foreach (var nav in root.GetSubKeyNames())
			{
				var rtc = root.OpenSubKey(string.Format(@"{0}\RoleTailored Client", nav));
				if (rtc != null)
				{
					var path = string.Empty;
					switch (nav)
					{
						case NAV2009:
							if (rtc.GetSubKeyNames().Length > 0)
							{
								var sub = rtc.OpenSubKey(rtc.GetSubKeyNames()[0]);
								if (sub != null)
								{
									path = sub.GetValue("Path").ToString();
								}
							}
							break;
						case NAV2013:
						case NAV2013R2:
							path = rtc.GetValue("Path").ToString();
							break;
					}
					if (!string.IsNullOrEmpty(path))
					{
						path = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(path)));
						if (!paths.Contains(path))
						{
							paths.Add(path);
						}
					}
				}
			}

			var versions = new SortedDictionary<string, NavVersion>();
			foreach (var path in paths)
			{
				var directories = Directory.GetDirectories(path, @"RoleTailored Client", SearchOption.AllDirectories);
				foreach (var directory in directories)
				{
					var rtc = Path.Combine(directory, "Microsoft.Dynamics.Nav.Client.exe");
					if (File.Exists(rtc))
					{
						var version = new NavVersion(rtc);
						versions.Add(version.FilePath, version);
					}
				}
			}

			foreach (var version in versions.Values)
			{
				this.listVersion.Items.Add(version);
			}
		}

		private string NavRootKey
		{
			get
			{
				if (Is64Bit())
				{
					return @"Software\Wow6432Node\Microsoft\Microsoft Dynamics NAV";
				}
				return @"Software\Microsoft\Microsoft Dynamics NAV";
			}
		}

		private static bool Is64Bit()
		{
			return (IntPtr.Size == 8);
		}

		private void buttonApply_Click(object sender, EventArgs e)
		{
			if (this.listVersion.SelectedIndex < 0)
			{
				MessageBox.Show("Select a version from the list before apply", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}

			var keyPath = string.Empty;
			var version = this.listVersion.SelectedItem as NavVersion;
			if (version != null)
			{
				// remove Interface GUID
				keyPath = string.Format(@"Interface\{0}", version.Interface.ToString("B"));
				if(Registry.ClassesRoot.OpenSubKey(keyPath) != null)
				{
					Registry.ClassesRoot.DeleteSubKeyTree(keyPath);
				}
				// remove TypeLib GUID
				keyPath = string.Format(@"TypeLib\{0}\{1}.{2}", version.TypeLib.ToString("B"), version.Info.ProductMajorPart, version.Info.ProductMinorPart);
				if(Registry.ClassesRoot.OpenSubKey(keyPath) != null)
				{
					Registry.ClassesRoot.DeleteSubKeyTree(keyPath);
				}

				if (Is64Bit())
				{
					// remove Interface GUID
					keyPath = string.Format(@"Wow6432Node\Interface\{0}", version.Interface.ToString("B"));
					if (Registry.ClassesRoot.OpenSubKey(keyPath) != null)
					{
						Registry.ClassesRoot.DeleteSubKeyTree(keyPath);
					}
					// remove TypeLib GUID
					keyPath = string.Format(@"Wow6432Node\TypeLib\{0}\{1}.{2}", version.TypeLib.ToString("B"), version.Info.ProductMajorPart, version.Info.ProductMinorPart);
					if (Registry.ClassesRoot.OpenSubKey(keyPath) != null)
					{
						Registry.ClassesRoot.DeleteSubKeyTree(keyPath);
					}
				}

				var icon = Registry.ClassesRoot.OpenSubKey(@"DYNAMICSNAV\DefaultIcon", true);
				if (icon == null)
				{
					icon = Registry.ClassesRoot.CreateSubKey(@"DYNAMICSNAV\DefaultIcon");
				}
				icon.SetValue("", version.FilePath + ",0", RegistryValueKind.String);

				var command = Registry.ClassesRoot.OpenSubKey(@"DYNAMICSNAV\Shell\Open\Command", true);
				if (command == null)
				{
					command = Registry.ClassesRoot.CreateSubKey(@"DYNAMICSNAV\Shell\Open\Command");
				}
				command.SetValue("", version.Command, RegistryValueKind.String);

				var process = new Process();
				process.StartInfo.Arguments = string.Format(@" /register ""{0}"" /tlb", Path.Combine(version.FolderPath, "Microsoft.Dynamics.Nav.Client.WinForms.dll"));
				process.StartInfo.CreateNoWindow = true;
				process.StartInfo.FileName = version.RegAsm;
				process.StartInfo.RedirectStandardOutput = true;
				process.StartInfo.RedirectStandardError = true;
				process.StartInfo.StandardErrorEncoding = Encoding.GetEncoding(850);
				process.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(850);
				process.StartInfo.UseShellExecute = false;
				if (Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher
				{
					//run as administrator
					process.StartInfo.Verb = "runas";
				}
				process.Start();
				var output = process.StandardOutput.ReadToEnd();
				var error = process.StandardError.ReadToEnd();
				if (!string.IsNullOrEmpty(error))
				{
					MessageBox.Show(error, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				else
				{
					MessageBox.Show(output, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
				process.Close();
			}
		}
	}
}
